﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SafeScript.MetaModel;
using SafeScript.MetaModel.Statements;
using SafeScript.MetaModel.Xml;

namespace SafeScript.Generator
{
    public enum OutputType
    {
        Standalone = 0,
        Wave = 1
    }

    public class JavaScriptGenerator
    {
        private JavaScriptWriter writer;
        private StreamWriter streamWriter;
        private string componentNamespace;
        private Dictionary<INamed, string> names = new Dictionary<INamed, string>();
        private Dictionary<INamed, string> namesIgnoringOverride = new Dictionary<INamed, string>();
        private int uniqueNameCounter = 1;
        private List<AbstractType> generatedTypes = new List<AbstractType>();
        private bool inIterator = false;
        private CaseLabel iteratorBreakCaseLabel;
        private CaseLabel iteratorContinueCaseLabel;
        private bool debug = false;
        /// <summary>
        /// Can be PropertySetter, PropertyGetter, MemberFunction, InlineFunction, FunctionOperator, Namespace, Class
        /// </summary>
        private object context;
        private OutputType outputType;

        public JavaScriptGenerator(string outfile, string componentNamespace, OutputType outputType)
        {
            this.outputType = outputType;
            this.componentNamespace = componentNamespace;
            FileStream stream = new FileStream(outfile, FileMode.Create);
            this.streamWriter = new StreamWriter(stream);
            this.writer = new JavaScriptWriter(this.streamWriter);
        }

        public void Close()
        {
            this.streamWriter.Close();
        }

        public void Generate(Namespace globalNamespace, ErrorReport report)
        {
            //var dummy = writer + "var" + this.componentNamespace + "=" + "{" + "}" + ";";

            BufferedJavaScriptWriter nspaceWriter = new BufferedJavaScriptWriter();
            this.GenerateNamespace(globalNamespace, report, nspaceWriter);
            this.writer.Write(nspaceWriter);

            if (this.outputType == OutputType.Wave)
            {
                BufferedJavaScriptWriter waveWriter = new BufferedJavaScriptWriter();
                var dummy = waveWriter + "function init() { if (wave && wave.isInWaveContainer()) { wave.setStateCallback(onWaveStateChange); } }";
                dummy = waveWriter + "gadgets.util.registerOnLoadHandler(init);";
                dummy = waveWriter + "var init_done = false; onWaveStateChange = function() { if ( !init_done ) { init_done = true; $System.onload() } };";
                this.writer.Write(waveWriter);
            }
        }

        private StringList GetQualifiedReadableName(Namespace nspace)
        {
            if (nspace is Class)
            {
                Class cl = nspace as Class;
                if (cl is BuiltinType)                    
                {
                    StringList l = new StringList();
                    l.Add(cl.Name);
                    return l;
                }
                else if (cl is TemplateInstanceTypeDef)
                {
                    TemplateInstanceTypeDef templ = cl as TemplateInstanceTypeDef;
                    return this.GetQualifiedReadableName(templ.Template as AbstractType);
                }
            }

            StringList lst = new StringList();

            Namespace n = nspace;
            while (n.ParentScope is Namespace)
            {
                if (n != nspace)
                {
                    lst.Insert(0, ".");
                    lst.Insert(0, n.Name);
                }
                else
                    lst.Insert(0, n.Name);
                n = n.ParentScope as Namespace;
            }

            return lst;
        }

        private StringList GetQualifiedName(Namespace nspace)
        {
            if (nspace is Class)
            {
                Class cl = nspace as Class;
                if (cl is BuiltinType)
                {
                    StringList l = new StringList();
                    l.Add("$System");
                    l.Add(".");
                    l.Add(Ident(cl));
                    return l;
                }
                else if ( cl is TemplateInstanceTypeDef )
                {
                    TemplateInstanceTypeDef templ = cl as TemplateInstanceTypeDef;
                    return this.GetQualifiedName(templ.Template as AbstractType);
                }
            }

            StringList lst = new StringList();
            lst.Add(this.componentNamespace);

            Namespace n = nspace;
            while (n.ParentScope is Namespace)
            {
                lst.Insert(1, ".");
                lst.Insert(2, Ident(n));
                n = n.ParentScope as Namespace;
            }

            return lst;
        }

        public string Ident(INamed obj)
        {
            return this.Ident(obj, false);
        }

        public string Ident(INamed obj, bool ignoringOverride)
        {
            JSNativeFunctionAttribute attrib = null;
            HTMLPropertyAttribute htmlProp = null;

            // Always choose the name of the base function.
            // Otherwise overriding functions would end up having a different name.
            if (obj is MemberFunction)
            {
                MemberFunction func = obj as MemberFunction;
                while (func.OverridenFunction != null && !ignoringOverride)
                    func = func.OverridenFunction;
                obj = func;

                // Use the function of the template instead
                while (func.Template != null)
                    func = func.Template;
                obj = func;
            }
            else if (obj is PropertyGetter)
            {
                PropertyGetter getter = obj as PropertyGetter;
                while (getter.OverridenGetter != null && !ignoringOverride)
                    getter = getter.OverridenGetter;

                Property prop = getter.Property;
                // Use the property of the template instead
                while (prop.Template != null)
                    prop = prop.Template as Property;
                getter = prop.Getter;

                htmlProp = getter.Property.GetAttribute<HTMLPropertyAttribute>();
                while (getter.OverridenGetter != null)
                    getter = getter.OverridenGetter;
                obj = getter;    
            }
            else if (obj is PropertySetter)
            {
                PropertySetter setter = obj as PropertySetter;
                while (setter.OverridenSetter != null && !ignoringOverride)
                    setter = setter.OverridenSetter;

                Property prop = setter.Property;
                // Use the property of the template instead
                while (prop.Template != null)
                    prop = prop.Template as Property;
                setter = prop.Setter;

                htmlProp = setter.Property.GetAttribute<HTMLPropertyAttribute>();
                while (setter.OverridenSetter != null)
                    setter = setter.OverridenSetter;
                obj = setter;
            }
            else if (obj is MemberVariable)
            {
                MemberVariable v = obj as MemberVariable;
                attrib = v.GetAttribute<JSNativeFunctionAttribute>();

                // Use the variable of the template instead
                while (v.Template != null)
                    v = v.Template;
                obj = v;
            }

            string str = null;
            if (ignoringOverride)
            {
                if (this.namesIgnoringOverride.TryGetValue(obj, out str))
                    return str;
            }
            else
            {
                if (this.names.TryGetValue(obj, out str))
                    return str;
            }

            if (attrib != null)
                str = attrib.JSCode;
            else if (htmlProp != null)
                str = htmlProp.HTMLName;
            else
            {
                str = "";
                string name = obj.Name;
                for (int i = 0; i < name.Length; ++i)
                {
                    if (char.IsLetterOrDigit(name[i]))
                        str += name[i];
                    else
                        str += "_";
                }
                str += "__";
                str += this.uniqueNameCounter.ToString();
                this.uniqueNameCounter++;
            }

            if (ignoringOverride)
                this.namesIgnoringOverride[obj] = str;
            else
                this.names[obj] = str;
            return str;
        }

        private void GenerateNamespace(Namespace nspace, ErrorReport report, BufferedJavaScriptWriter nspaceWriter)
        {
            object oldcontext = this.context;
            this.context = nspace;

            var dummy = nspaceWriter + this.GetQualifiedName(nspace) + "=" + "{}" + ";";

            foreach (string key in nspace.ScopeElements.Keys)
            {
                object obj = nspace.ScopeElements[key];
                if (obj is Class)
                {
                    this.GenerateClass(obj as Class, report, nspaceWriter);
                }
                else if (obj is Interface)
                {
                    this.GenerateInterface(obj as Interface, report, nspaceWriter);
                }
                else if (obj is EnumTypeDef)
                {
                    // Do nothing by intention
                }
                else if (obj is Namespace)
                {
                    this.GenerateNamespace(obj as Namespace, report, nspaceWriter);
                }
                else if (obj is MemberFunction)
                {
                    MemberFunction func = obj as MemberFunction;
                    if (func.IsAbstract)
                        continue;
                    if (func.GetAttribute<JSNativeFunctionAttribute>() != null)
                        continue;
                    GenerateMemberFunction(nspace, func, nspaceWriter, null);
                }
                else if (obj is MemberFunctionSet)
                {
                    foreach (MemberFunction func in (obj as MemberFunctionSet).Functions)
                    {
                        if (func.IsAbstract)
                            continue;
                        if (func.GetAttribute<JSNativeFunctionAttribute>() != null)
                            continue;
                        GenerateMemberFunction(nspace, func, nspaceWriter, null);
                    }
                }
                else if (obj is Property)
                {
                    Property prop = obj as Property;
                    if (prop.GetAttribute<JSNativePropertyAttribute>() != null)
                        continue;
                    GenerateProperty(nspace, prop, nspaceWriter, null);
                }
                else if (obj is MemberVariable)
                {
                    if ((obj as MemberVariable).GetAttribute<JSNativeFunctionAttribute>() != null)
                        continue;
                    MemberVariable mv = obj as MemberVariable;
                    GenerateMemberVariable(nspace, mv, nspaceWriter, null);
                } 

                // else TODO
            }

            // Register statements which are executed as the module loads
            if (nspace.Statements != null)
            {
                dummy = nspaceWriter + "$System.onloadHandlers.push" + "(" + "function" + "(" + ")" + "{";

                if (this.debug)
                    dummy = dummy + "try" + "{" + "var" + "$L" + ";";

                this.GenerateStatementList(nspace, nspace.Statements, nspaceWriter);

                if (this.debug)
                {
                    dummy = dummy + "}" + "catch" + "(" + "$e" + ")" + "{";
                    dummy = dummy + "$e = {" + "$func" + ":";
                    dummy.WriteString(this.GetQualifiedReadableName(nspace).ToString());
                    dummy = dummy + "," + "$line" + ":" + "$L" + "," + "$exception" + ":" + "$e" + "};";
                    dummy = dummy + "$System.Debug.printException($e);";
                    dummy = dummy + "}";
                }

                dummy = nspaceWriter + "}" + ")" + ";";
            }

            this.context = oldcontext;
        }

        private void GenerateInterface(Interface iface, ErrorReport report, BufferedJavaScriptWriter nspaceWriter)
        {
            if (this.generatedTypes.Contains(iface))
                return;
            this.generatedTypes.Add(iface);

            object oldcontext = this.context;
            this.context = iface;

            var dummy = nspaceWriter + this.GetQualifiedName(iface) + "=" + "function" + "(" + ")" + "{" + "}" + ";";

            this.context = oldcontext;
        }

        private void GenerateClass(Class cl, ErrorReport report, BufferedJavaScriptWriter nspaceWriter)
        {
            // Do not generate builtin types
            if (cl is BuiltinType)
                return;

            // Do not generate template instances
            if (cl is TemplateInstanceTypeDef)
            {
                // Generate the template class instead
                this.GenerateClass((cl as TemplateInstanceTypeDef).Template as Class, report, nspaceWriter);
                return;
            }

            // Do not generate native classes.
            if (cl.GetAttribute<JSNativeClassAttribute>() != null || cl.GetAttribute<HTMLClassAttribute>() != null)
            {
                //if (!cl.IsSealed)
                    //report.Add(new ErrorEntry(cl.Node) { Info = cl.Name, Type = ErrorType.NativeClassMustBeSealed });
                if (!cl.IsAbstract)
                    report.Add(new ErrorEntry(cl.Node) { Info = cl.Name, Type = ErrorType.NativeClassMustBeAbstract });
                // A native class may only derive from other native classes
                if( cl.BaseClass != null && !(cl.BaseClass is ObjectType) && cl.BaseClass.GetAttribute<JSNativeClassAttribute>() == null && cl.BaseClass.GetAttribute<HTMLClassAttribute>() == null )
                    report.Add(new ErrorEntry(cl.Node) { Info = cl.Name, Type = ErrorType.NativeClassMustNotInherit });

                // TODO: All members must be flagged as Native. Do the error checking.
                return;
            }

            // Already generated?
            if (this.generatedTypes.Contains(cl))
                return;
            this.generatedTypes.Add(cl);

            object oldcontext = this.context;
            this.context = cl;

            // Generate all base types
            foreach (AbstractType at in cl.BaseTypes)
            {
                if (at is Class)
                {
                    // If this is a nested class, we must generate the surrounding class first
                    Class c = at as Class;
                    while (c.ParentScope is Class && !this.generatedTypes.Contains(c.ParentScope as Class))
                        c = c.ParentScope as Class;
                    this.GenerateClass(c, report, nspaceWriter);
                }
                else if (at is Interface)
                {
                    Interface iface = at as Interface;
                    // If this interfaces is a nested interface, we must generate the surrounding class first
                    if (iface.ParentScope is Class && !this.generatedTypes.Contains(iface.ParentScope as Class))
                    {
                        Class c = iface.ParentScope as Class;
                        while (c.ParentScope is Class && !this.generatedTypes.Contains(c.ParentScope as Class))
                            c = c.ParentScope as Class;
                        this.GenerateClass(c, report, nspaceWriter);
                    }
                    else
                        this.GenerateInterface(at as Interface, report, nspaceWriter);
                }
                else
                    System.Diagnostics.Debug.Assert(false, "Unknown kind of base class");
            }

            BufferedJavaScriptWriter staticFuncWriter = new BufferedJavaScriptWriter();

            // Create the default constructor
            BufferedJavaScriptWriter defCtorWriter = new BufferedJavaScriptWriter();
            var dummy = defCtorWriter + this.GetQualifiedName(cl) + "=" + "function" + "(" + ")" + "{";            
            if (cl.BaseClass != null && !(cl.BaseClass is BuiltinType))
            {
                dummy = defCtorWriter + GetQualifiedName(cl.BaseClass) + "." + "call" + "(" + "this" + ")" + ";";
            }
            
            // Call $System.extend with the base classes and interfaces
            BufferedJavaScriptWriter memberWriter = new BufferedJavaScriptWriter();
            dummy = memberWriter + "$System" + "." + "extend" + "(" + this.GetQualifiedName(cl) + ",";
            if (cl.BaseClass != null && !(cl.BaseClass is ObjectType))
                dummy = memberWriter + this.GetQualifiedName(cl.BaseClass);
            else
                dummy = memberWriter + "null";
            dummy = memberWriter + ", [";
            bool first = true;
            foreach (AbstractType at in cl.BaseTypes)
            {
                if (at is Interface)
                {
                    if (first)
                        first = false;
                    else
                        dummy = memberWriter + ",";
                    dummy = memberWriter + GetQualifiedName(at);
                }
            }
            dummy = memberWriter + "]," + "{";

            // Generate all members
            foreach (string key in cl.ScopeElements.Keys)
            {
                object obj = cl.ScopeElements[key];
                if (obj is Class)
                {
                    // Do nothing by intention
                }
                else if (obj is Interface)
                {
                    // Do nothing by intention
                }
                else if (obj is MemberFunction)
                {
                    MemberFunction func = obj as MemberFunction;
                    if (func.IsAbstract)
                        continue;
                    if (func.GetAttribute<JSNativeFunctionAttribute>() != null)
                        continue;
                    if (func is Constructor)
                        GenerateConstructor(cl, func as Constructor, staticFuncWriter, memberWriter);
                    else
                        GenerateMemberFunction(cl, func, staticFuncWriter, memberWriter);
                }
                else if (obj is MemberFunctionSet)
                {
                    foreach (MemberFunction func in (obj as MemberFunctionSet).Functions)
                    {
                        if (func.IsAbstract)
                            continue;
                        if (func.GetAttribute<JSNativeFunctionAttribute>() != null)
                            continue;
                        if (func is Constructor)
                            GenerateConstructor(cl, func as Constructor, staticFuncWriter, memberWriter);
                        else
                            GenerateMemberFunction(cl, func, staticFuncWriter, memberWriter);
                    }
                }
                else if (obj is Property)
                {
                    Property prop = obj as Property;
                    GenerateProperty(cl, prop, staticFuncWriter, memberWriter);
                }
                else if (obj is MemberVariable)
                {
                    MemberVariable mv = obj as MemberVariable;
                    GenerateMemberVariable(cl, mv, staticFuncWriter, defCtorWriter);
                } 
            }

            dummy = memberWriter + "$type" + ":" + GetQualifiedName(cl) + "}" + ")" + ";";

            // Each overridable members are added under a second name to access the base implementations
            foreach (string key in cl.ScopeElements.Keys)
            {
                object obj = cl.ScopeElements[key];
                if (obj is MemberFunction)
                {
                    MemberFunction func = (obj as MemberFunction);
                    if (func.GetAttribute<JSNativeFunctionAttribute>() != null || func is Constructor)
                        continue;
                    dummy = memberWriter + this.GetQualifiedName(cl) + "." + "prototype" + "." + Ident(func, true) + "=" + this.GetQualifiedName(cl) + "." + "prototype" + "." + Ident(func, false) + ";";
                }
                else if (obj is MemberFunctionSet)
                {
                    foreach (MemberFunction f in (obj as MemberFunctionSet).Functions)
                    {
                        if (f.GetAttribute<JSNativeFunctionAttribute>() != null || f is Constructor)
                            continue;
                        dummy = memberWriter + this.GetQualifiedName(cl) + "." + "prototype" + "." + Ident(f, true) + "=" + this.GetQualifiedName(cl) + "." + "prototype" + "." + Ident(f, false) + ";";
                    }
                }
                else if (obj is Property)
                {
                    Property prop = obj as Property;
                    if (prop.GetAttribute<JSNativePropertyAttribute>() != null)
                        continue;
                    if (prop.Getter != null)                    
                        dummy = memberWriter + this.GetQualifiedName(cl) + "." + "prototype" + "." + Ident(prop.Getter, true) + "=" + this.GetQualifiedName(cl) + "." + "prototype" + "." + Ident(prop.Getter, false) + ";";
                    if (prop.Setter != null)
                        dummy = memberWriter + this.GetQualifiedName(cl) + "." + "prototype" + "." + Ident(prop.Setter, true) + "=" + this.GetQualifiedName(cl) + "." + "prototype" + "." + Ident(prop.Setter, false) + ";";
                }
            }

            dummy = defCtorWriter + "}" + ";";

            nspaceWriter.Write(defCtorWriter);
            nspaceWriter.Write(staticFuncWriter);
            nspaceWriter.Write(memberWriter);

            // Generate nested classes
            foreach (string key in cl.ScopeElements.Keys)
            {
                object obj = cl.ScopeElements[key];
                if (obj is Class)
                {
                    this.GenerateClass(obj as Class, report, nspaceWriter);
                }
                else if (obj is Interface)
                {
                    this.GenerateInterface(obj as Interface, report, nspaceWriter);
                }
            }

            this.context = oldcontext;
        }

        private void GenerateMemberVariable(Namespace cl, MemberVariable mv, BufferedJavaScriptWriter staticFuncWriter, BufferedJavaScriptWriter defCtorWriter)
        {
            object oldcontext = this.context;
            this.context = mv;

            JavaScriptWriter dummy = null;
            if (mv.InitialValue != null)
            {
                if (mv.IsStatic)
                {
                    dummy = staticFuncWriter + GetQualifiedName(cl) + "." + Ident(mv) + "=";
                    this.GenerateExpression(cl, mv.InitialValue, staticFuncWriter);
                    staticFuncWriter.WriteOperator(";");
                }
                else
                {
                    dummy = defCtorWriter + "this" + "." + Ident(mv) + "=";
                    this.GenerateExpression(cl, mv.InitialValue, defCtorWriter);
                    defCtorWriter.WriteOperator(";");
                }
            }

            this.context = oldcontext;
        }

        private void GenerateProperty(Namespace cl, Property prop, BufferedJavaScriptWriter staticFuncWriter, BufferedJavaScriptWriter memberWriter)
        {
            object oldcontext = this.context;

            JavaScriptWriter dummy = null;
            if (prop.GetAttribute<WaveStatePropertyAttribute>() != null)
            {
                System.Diagnostics.Debug.Assert(prop.IsStatic);
                if (prop.Getter != null && prop.Getter.Owner == cl)
                {
                    this.context = prop.Getter;

                    dummy = staticFuncWriter + GetQualifiedName(cl) + "." + Ident(prop.Getter) + "=" + "function" + "(" + ")" + "{";
                    if (prop.Type.CanBeAssignedTo(BoolType.Self) || prop.Type.CanBeAssignedTo(NumericType.Self) || prop.Type.CanBeAssignedTo(StringType.Self))
                        dummy = staticFuncWriter + ("return wave.getState().get('" + GetQualifiedName(cl).ToString() + "." + Ident(prop) + "', '');");
                    else
                        dummy = staticFuncWriter + ("return gadgets.json.parse(wave.getState().get('" + GetQualifiedName(cl).ToString() + "." + Ident(prop) + "', ''));");
                    dummy = staticFuncWriter + "}" + ";";
                }
                if (prop.Setter != null && prop.Setter.Owner == cl)
                {
                    this.context = prop.Setter;

                    dummy = staticFuncWriter + GetQualifiedName(cl) + "." + Ident(prop.Setter) + "=" + "function" + "(" + "value" + ")" + "{";
                    if (prop.Type.CanBeAssignedTo(BoolType.Self) || prop.Type.CanBeAssignedTo(NumericType.Self) || prop.Type.CanBeAssignedTo(StringType.Self))
                        dummy = staticFuncWriter + ("wave.getState().submitDelta({'" + GetQualifiedName(cl).ToString() + "." + Ident(prop) + "': value});");
                    else
                        dummy = staticFuncWriter + ("wave.getState().submitDelta({'" + GetQualifiedName(cl).ToString() + "." + Ident(prop) + "': gadgets.json.stringify(value)});");
                    dummy = staticFuncWriter + "}" + ";";

                    dummy = staticFuncWriter + GetQualifiedName(cl) + "." + (Ident(prop.Setter) + "_notify") + "=" + "function" + "(" + "value" + ")" + "{";
                    dummy = staticFuncWriter + "if" + "(" + (Ident(prop) + "_") + ")" + "$System.publishProperty.call";
                    dummy = staticFuncWriter + "(" + (Ident(prop) + "_") + ",";
                    dummy = staticFuncWriter + Ident(prop.Getter) + "(" + ")";
                    dummy = staticFuncWriter + ")" + ";";
                    dummy = staticFuncWriter + "}" + ";";
                }
            }
            else if (prop.IsStatic)
            {
                if (prop.Getter != null && !prop.Getter.IsAbstract && prop.Getter.Owner == cl)
                {
                    this.context = prop.Getter;

                    dummy = staticFuncWriter + GetQualifiedName(cl) + "." + Ident(prop.Getter) + "=" + "function" + "(" + ")" + "{";
                    if (prop.Getter.Statements == null)
                        dummy = staticFuncWriter + "return" + GetQualifiedName(cl) + "." + Ident(prop) + ";";
                    else
                        this.GenerateStatementList(cl, prop.Getter.Statements, staticFuncWriter);
                    dummy = staticFuncWriter + "}" + ";";
                }
                if (prop.Setter != null && !prop.Setter.IsAbstract && prop.Setter.Owner == cl)
                {
                    this.context = prop.Setter;

                    dummy = staticFuncWriter + GetQualifiedName(cl) + "." + Ident(prop.Setter) + "=" + "function" + "(" + "value" + ")" + "{";
                    if (prop.Setter.Statements == null)
                        dummy = staticFuncWriter + GetQualifiedName(cl) + "." + Ident(prop) + "=" + "value" + ";";
                    else
                        this.GenerateStatementList(cl, prop.Setter.Statements, staticFuncWriter);

                    // TODO: Insert in all returns
                    dummy = staticFuncWriter + "if" + "(" + (Ident(prop)+"_") + ")" + "$System.publishProperty.call";
                    dummy = staticFuncWriter + "(" + (Ident(prop) + "_") + ",";
                    // dummy = staticFuncWriter + "this" + "." + Ident(prop);
                    dummy = staticFuncWriter + Ident(prop.Getter) + "(" + ")";
                    dummy = staticFuncWriter + ")" + ";";

                    dummy = staticFuncWriter + "}" + ";";
                }
            }
            else
            {
                if (prop.Getter != null && !prop.Getter.IsAbstract && prop.Getter.Owner == cl)
                {
                    this.context = prop.Getter;

                    dummy = memberWriter + Ident(prop.Getter) + ":" + "function" + "(" + ")" + "{";
                    if (prop.Getter.Statements == null)
                        dummy = memberWriter + "return" + "this" + "." + Ident(prop) + ";";
                    else
                        this.GenerateStatementList(cl, prop.Getter.Statements, memberWriter);
                    dummy = memberWriter + "}" + ",";
                    memberWriter.WriteLine();
                }
                if (prop.Setter != null && !prop.Setter.IsAbstract && prop.Setter.Owner == cl)
                {
                    this.context = prop.Setter;

                    dummy = memberWriter + Ident(prop.Setter) + ":" + "function" + "(" + "value" + ")" + "{";
                    if (prop.Setter.Statements == null)
                        dummy = memberWriter + "this" + "." + Ident(prop) + "=" + "value" + ";";
                    else
                        this.GenerateStatementList(cl, prop.Setter.Statements, memberWriter);

                    // TODO: Insert in all returns
                    dummy = memberWriter + "if" + "(" + "this" + "." + (Ident(prop) + "_") + ")" + "$System.publishProperty.call";
                    dummy = memberWriter + "(" + "this" + "." + (Ident(prop) + "_") + "," + "this" + "." + Ident(prop.Getter) + "(" + ")" + ")" + ";";

                    dummy = memberWriter + "}" + ",";
                    memberWriter.WriteLine();
                }
            }

            this.context = oldcontext;
        }

        private void GenerateConstructor(Namespace cl, Constructor ctor, BufferedJavaScriptWriter staticFuncWriter, BufferedJavaScriptWriter memberWriter)
        {
            // Do not generate the generic constructor
            if (ctor.IsGenericConstructor)
                return;

            object oldcontext = this.context;
            this.context = ctor;

            var dummy = memberWriter + Ident(ctor) + ":" + "function" + "(";
            bool first = true;
            foreach (Parameter p in ctor.Parameters)
            {
                if (first)
                    first = false;
                else
                    memberWriter.WriteOperator(",");
                memberWriter.WriteIdentifier(Ident(p));
            }
            dummy = memberWriter + ")" + "{";

            // Call the base constructor
            if (ctor.BaseConstructor != null)
            {
                dummy = memberWriter + "this" + "." + Ident(ctor.BaseConstructor) + "(";
                first = true;
                foreach (Operator op in ctor.SuperParameters)
                {
                    if (first)
                        first = false;
                    else
                        memberWriter.WriteOperator(",");
                    this.GenerateExpression(cl, op, memberWriter);
                }
                dummy = memberWriter + ")" + ";";
            }

            this.GenerateStatementList(cl, ctor.Statements, memberWriter);

            dummy = memberWriter + "return" + "this" + ";" + "}" + ",";

            this.context = oldcontext;
        }

        private void GenerateMemberFunction(Namespace cl, MemberFunction func, BufferedJavaScriptWriter staticFuncWriter, BufferedJavaScriptWriter memberWriter)
        {
            object oldcontext = this.context;
            this.context = func;

            JavaScriptWriter dummy = null;
            if (func.IsStatic)
            {
                dummy = staticFuncWriter + GetQualifiedName(cl) + "." + Ident(func) + "=" + "function" + "(";
                bool first = true;
                foreach (Parameter p in func.Parameters)
                {
                    if (first)
                        first = false;
                    else
                        staticFuncWriter.WriteOperator(",");
                    staticFuncWriter.WriteIdentifier(Ident(p));
                }
                dummy = staticFuncWriter + ")" + "{";

                if (this.debug)
                    dummy = staticFuncWriter + "try" + "{" + "var" + "$L" + ";" ;

                this.GenerateStatementList(cl, func.Statements, staticFuncWriter);
            }
            else
            {
                dummy = memberWriter + Ident(func) + ":" + "function" + "(";
                bool first = true;
                foreach (Parameter p in func.Parameters)
                {
                    if (first)
                        first = false;
                    else
                        memberWriter.WriteOperator(",");
                    memberWriter.WriteIdentifier(Ident(p));
                }
                dummy = memberWriter + ")" + "{";

                if (this.debug)
                    dummy = memberWriter + "try" + "{" + "var" + "$L" + ";";
                this.GenerateStatementList(cl, func.Statements, memberWriter);
            }

            if (this.debug)
            {
                dummy = dummy + "}" + "catch" + "(" + "$e" + ")" + "{";
                dummy = dummy + "throw{" + "$func" + ":";
                dummy.WriteString(this.GetQualifiedReadableName(func.Owner).ToString() + "." + func.Name);
                dummy = dummy + "," + "$line" + ":" + "$L" + "," + "$exception" + ":" + "$e";
                foreach (Parameter p in func.Parameters)
                {
                    dummy.WriteOperator(",");
                    dummy.WriteString(p.Name);
                    dummy.WriteOperator(":");
                    dummy.WriteIdentifier(Ident(p));
                }
                foreach (VarStatement v in func.Variables)
                {
                    dummy.WriteOperator(",");
                    dummy.WriteString(v.Name);
                    dummy.WriteOperator(":");
                    dummy.WriteIdentifier(Ident(v));
                }
                dummy = dummy + "};" + "}";
            }

            if (func.IsStatic)
            {
                dummy = staticFuncWriter + "}" + ";";
            }
            else
            {
                dummy = memberWriter + "}" + ",";
            }

            this.context = oldcontext;
        }

        private void GenerateExpression(Namespace nspace, Operator op, BufferedJavaScriptWriter writer)
        {
            this.GenerateExpression(nspace, op, writer, false, int.MaxValue);
        }

        private void GenerateExpression(Namespace nspace, Operator op, BufferedJavaScriptWriter writer, bool propertyReference)
        {
            this.GenerateExpression(nspace, op, writer, propertyReference, int.MaxValue);
        }

        private void GenerateExpression(Namespace nspace, Operator op, BufferedJavaScriptWriter writer, bool propertyReference, int operatorPrecedence)
        {
            if (op.GetPrecedence() > operatorPrecedence)
                writer.WriteOperator("(");

            if (op is AtomIdentifier)
            {
                AtomIdentifier ai = op as AtomIdentifier;
                GenerateAtomIdentifier(nspace, ai, writer, propertyReference);
            }
            else if (op is AtomBoolValue)
            {
                writer.WriteBool((op as AtomBoolValue).Value);
            }
            else if (op is AtomNumValue)
            {
                writer.WriteFloat((op as AtomNumValue).Value);
            }
            else if (op is AtomNull)
            {
                writer.WriteIdentifier("null");
            }
            else if (op is AtomStringValue)
            {
                writer.WriteString((op as AtomStringValue).Value);
            }
            else if (op is Atom)
            {
                switch ((op as Atom).Kind)
                {
                    case AtomKind.SUPER:
                        writer.WriteIdentifier("this");
                        break;
                    case AtomKind.THIS:
                        writer.WriteIdentifier("this");
                        break;
                    case AtomKind.VALUE:
                        writer.WriteIdentifier("value");
                        break;
                    default:
                        System.Diagnostics.Debug.Assert(false, "Unexpected grammar rule");
                        break;
                }
            }
            else if (op is BinaryOperator)
            {
                this.GenerateBinaryOperator(nspace, op as BinaryOperator, writer);
            }
            else if (op is NewOperator)
            {
                this.GenerateNewOperator(nspace, op as NewOperator, writer);
            }
            else if (op is FunctionCallOperator)
            {
                this.GenerateFunctionCallOperator(nspace, op as FunctionCallOperator, writer);
            }
            else if (op is NotOperator)
            {
                this.GenerateNotOperator(nspace, op as NotOperator, writer);
            }
            else if (op is PlusOperator)
            {
                this.GeneratePlusOperator(nspace, op as PlusOperator, writer);
            }
            else if (op is MinusOperator)
            {
                this.GenerateMinusOperator(nspace, op as MinusOperator, writer);
            }
            else if (op is InvertOperator)
            {
                this.GenerateInvertOperator(nspace, op as InvertOperator, writer);
            }
            else if (op is IncDecOperator)
            {
                this.GenerateIncDecOperator(nspace, op as IncDecOperator, writer);
            }
            else if (op is PropertyOperator)
            {
                this.GeneratePropertyOperator(nspace, op as PropertyOperator, writer, propertyReference);
            }
            else if (op is AsOperator)
            {
                this.GenerateAsOperator(nspace, op as AsOperator, writer);
            }
            else if (op is IsOperator)
            {
                this.GenerateIsOperator(nspace, op as IsOperator, writer);
            }
            else if (op is ListContentOperator)
            {
                this.GenerateListOperator(nspace, op as ListContentOperator, writer);
            }
            else if (op is DictContentOperator)
            {
                this.GenerateDictOperator(nspace, op as DictContentOperator, writer);
            }
            else if (op is FunctionObjectOperator)
            {
                this.GenerateFunctionObjectOperator(nspace, op as FunctionObjectOperator, writer);
            }
            else if (op is InlineFunctionOperator)
            {
                this.GenerateInlineFunctionOperator(nspace, op as InlineFunctionOperator, writer);
            }
            else if (op is FunctionOperator)
            {
                this.GenerateFunctionOperator(nspace, op as FunctionOperator, writer);
            }
            else if (op is XmlTag)
            {
                this.GenerateXmlTag(nspace, op as XmlTag, writer);
            }
            else if (op is IndexOperator)
            {
                this.GenerateIndexOperator(nspace, op as IndexOperator, writer);
            }
            else if (op is BindingOperator)
            {
                this.GenerateBindingOperator(nspace, op as BindingOperator, writer);
            }
            else if (op is BindingObjectOperator)
            {
                this.GenerateBindingObjectOperator(nspace, op as BindingObjectOperator, writer);
            }
            else if (op is BindingTermOperator)
            {
                this.GenerateBindingTermOperator(nspace, op as BindingTermOperator, writer);
            }
            else
                System.Diagnostics.Debug.Assert(false, "Unsupported operator");
            // TODO

            if (op.GetPrecedence() > operatorPrecedence)
                writer.WriteOperator(")");
        }

        private void GenerateBindingTermOperator(Namespace nspace, BindingTermOperator bindingTermOperator, BufferedJavaScriptWriter writer)
        {
            // We can assume that we are running inside a function, which belongs to a binding

            var dummy = writer + "$System.subscribeBindingTerm" + "(";
            this.GenerateExpression(nspace, bindingTermOperator.Operator, writer, true);
            writer.WriteOperator(",");
            writer.WriteString(Ident(bindingTermOperator.Property) + "_");
            writer.WriteOperator(",");
            writer.WriteString(Ident(bindingTermOperator.Property.Getter));
            writer.WriteOperator(",");
            writer.WriteString(Ident(bindingTermOperator.Property.Setter));
            writer.WriteOperator(",");
            dummy = writer + "arguments.callee" + "." + "binding";
            writer.WriteOperator(")");
        }

        private void GenerateBindingObjectOperator(Namespace nspace, BindingObjectOperator bindingObjectOperator, BufferedJavaScriptWriter writer)
        {
            if (bindingObjectOperator.Operator is InlineFunctionOperator || bindingObjectOperator.Operator is FunctionOperator)
            {
                var dummy = writer + "$System.wrapFuncAsBinding" + "(" + "this" + ",";
                this.GenerateExpression(nspace, bindingObjectOperator.Operator, writer);
                dummy = writer + ")";
            }
            else
            {
                var dummy = writer + "$System.wrapExprAsBinding" + "(" + "this" + ",";
                dummy = writer + "function" + "(" + ")" + "{"; 
                dummy = writer + "$System.unsubscribeBindingTerm" + "(" + "arguments.callee" + "." + "binding" + ")" + ";" + "return";
                this.GenerateExpression(nspace, bindingObjectOperator.Operator, writer);
                writer.WriteOperator(";");
                writer.WriteOperator("}");
                dummy = writer + ")";
            }
        }

        private void GenerateBindingOperator(Namespace nspace, BindingOperator bindingOperator, BufferedJavaScriptWriter writer)
        {
            // The left side of the binding is a binding itself?
            if (bindingOperator.SourceBinding != null)
            {
                var dummy = writer + "$System.connectBindings" + "(" + "true" + ",";
                this.GenerateExpression(nspace, bindingOperator.Left, writer);
            }
            // The left side of the binding is a function?
            else if (bindingOperator.SourceFunc != null)
            {
                var dummy = writer + "$System.connectBindings" + "(" + "true" + ",";
                dummy = writer + "$System.wrapFuncAsBinding" + "(" + "this" + ",";
                this.GenerateExpression(nspace, bindingOperator.Left, writer);
                dummy = writer + ")";
            }
            // The left side of the binding is an event?
            else if (bindingOperator.SourceEvent != null)
            {
                if (bindingOperator.SourceEvent.GetAttribute("HTMLEvent") != null)
                {
                    this.GenerateExpression(nspace, bindingOperator.Left, writer, true);
                    var dummy = writer + "." + "addEventListener" + "(";
                    writer.WriteString((bindingOperator.SourceEvent.GetAttribute("HTMLEvent") as HTMLEventAttribute).HTMLName);
                }
                else
                {
                    var dummy = writer + "$System.connectBindings" + "(" + "true" + ",";
                    dummy = writer + "$System.wrapEventAsBinding" + "(";
                    this.GenerateExpression(nspace, bindingOperator.Left, writer, true);
                    dummy = writer + ",";
                    writer.WriteString(Ident(bindingOperator.SourceEvent));
                    dummy = writer + ")";
                }
            }
            // The left side of the binding is a property?
            else
            {
                System.Diagnostics.Debug.Assert(bindingOperator.SourceProperty != null, "Programming error");
                var dummy = writer + "$System.subscribeBinding" + "(";
                this.GenerateExpression(nspace, bindingOperator.Left, writer, true);
                writer.WriteOperator(",");
                writer.WriteString(Ident(bindingOperator.SourceProperty) + "_");
                writer.WriteOperator(",");
                writer.WriteString(Ident(bindingOperator.SourceProperty.Getter));
                writer.WriteOperator(",");
                writer.WriteString(Ident(bindingOperator.SourceProperty.Setter));
            }

            writer.WriteOperator(",");

            if (bindingOperator.SourceEvent != null && bindingOperator.SourceEvent.GetAttribute("HTMLEvent") != null)
            {
                if (bindingOperator.SinkBinding != null)
                {
                    this.GenerateExpression(nspace, bindingOperator.Right, writer);
                    var dummy = writer + "." + "invoke";
                }
                else if (bindingOperator.SinkFunc != null)
                {
                    this.GenerateExpression(nspace, bindingOperator.Right, writer);
                }
                else
                {
                    System.Diagnostics.Debug.Assert(bindingOperator.SinkProperty != null, "Programming error");
                    this.GenerateExpression(nspace, bindingOperator.Right, writer, true);
                    var dummy = writer + "." + Ident(bindingOperator.SinkProperty.Getter);
                }

                writer.WriteOperator(",");
                writer.WriteBool(false);
            }
            else
            {
                if (bindingOperator.SinkBinding != null)
                {
                    this.GenerateExpression(nspace, bindingOperator.Right, writer);
                }
                else if (bindingOperator.SinkFunc != null)
                {
                    var dummy = writer + "$System.wrapFuncAsBinding" + "(" + "this" + ",";
                    this.GenerateExpression(nspace, bindingOperator.Right, writer);
                    dummy = writer + ")";
                }
                else
                {
                    System.Diagnostics.Debug.Assert(bindingOperator.SinkProperty != null, "Programming error");
                    JavaScriptWriter dummy = null;
                    if (  bindingOperator.SinkProperty.GetAttribute<HTMLPropertyAttribute>() != null )
                        dummy = writer + "$System.wrapHTMLPropertyAsBinding" + "(";
                    else
                        dummy = writer + "$System.wrapPropertyAsBinding" + "(";
                    this.GenerateExpression(nspace, bindingOperator.Right, writer, true);
                    writer.WriteOperator(",");
                    writer.WriteString(Ident(bindingOperator.SinkProperty) + "_");
                    writer.WriteOperator(",");
                    writer.WriteString(Ident(bindingOperator.SinkProperty.Getter));
                    writer.WriteOperator(",");
                    writer.WriteString(Ident(bindingOperator.SinkProperty.Setter));
                    writer.WriteOperator(")");
                }
            }

            writer.WriteOperator(")");
        }

        private void GenerateAtomIdentifier(Namespace nsapce, AtomIdentifier ai, BufferedJavaScriptWriter writer, bool propertyReference)
        {
            if (ai.Object is Property)
            {
                Property prop = ai.Object as Property;
                HTMLPropertyAttribute attrib = prop.GetAttribute<HTMLPropertyAttribute>();
                JSNativePropertyAttribute attrib2 = prop.GetAttribute<JSNativePropertyAttribute>();

                // TODO Native

                if (prop.IsStatic)
                {
                    if (propertyReference)
                    {
                        // Do nothing by intention
                    }
                    else if (attrib2 != null)
                    {
                        var dummy = writer + attrib2.JSGetterCode;
                    }
                    else
                    {
                        var dummy = writer + Ident((ai.Object as Property).Getter) + "(" + ")";
                    }
                }
                else
                {
                    if (propertyReference)
                        writer.WriteIdentifier("this");
                    else if (attrib != null)
                    {
                        var dummy = writer + "this" + "." + attrib.HTMLName;
                    }
                    else if (attrib2 != null)
                    {
                        if (attrib2.IsMember)
                        {
                            var dummy = writer + "this" + "." + attrib2.JSGetterCode;
                        }
                        else
                        {
                            var dummy = writer + "this" + "." + attrib2.JSGetterCode + "(" + "this" + ")";
                        }
                    }
                    else
                    {
                        var dummy = writer + "this" + "." + Ident(prop.Getter) + "(" + ")";
                    }
                }
            }
            else if (ai.Object is MemberVariable)
            {
                // TODO Native

                MemberVariable mv = ai.Object as MemberVariable;
                // The member is static?
                if (mv.IsStatic)
                {
                    var dummy = writer + GetQualifiedName(mv.Owner) + "." + Ident(mv);
                }
                else
                {
                    writer.WriteIdentifier("this");
                    writer.WriteOperator(".");
                    writer.WriteIdentifier(Ident(mv));
                }
            }
            else if (ai.Object is MemberFunction)
            {
                MemberFunction func = ai.Object as MemberFunction;
                JSNativeFunctionAttribute attrib = func.GetAttribute<JSNativeFunctionAttribute>();
                if (attrib != null)
                {
                    // The member is static?
                    if (func.IsStatic)
                    {
                        var dummy = writer + attrib.JSCode;
                    }
                    else if (attrib.IsMember)
                    {
                        writer.WriteIdentifier("this");
                        writer.WriteOperator(".");
                        writer.WriteIdentifier(attrib.JSCode);
                    }
                    else
                    {
                        var dummy = writer + "$System.inlineFunc" + "(";
                        writer.WriteIdentifier(attrib.JSCode);
                        writer.WriteOperator(",");
                        writer.WriteOperator("this");
                        writer.WriteOperator(")");
                    }
                }
                else
                {
                    // The member is static?
                    if (func.IsStatic)
                    {
                        var dummy = writer + GetQualifiedName(func.Owner) + "." + Ident(func);
                    }
                    else
                    {
                        writer.WriteIdentifier("this");
                        writer.WriteOperator(".");
                        writer.WriteIdentifier(Ident(func));
                    }
                }
            }
            else if (ai.Object is Namespace)
            {
                var dummy = writer + GetQualifiedName(ai.Object as Namespace);
            }
            else if (ai.Object is VarStatement) // Local variable
            {
                VarStatement v = ai.Object as VarStatement;
                if (v.IsInInterator)
                {
                    var dummy = writer + "$s" + "." + Ident(v);
                }
                else
                    writer.WriteIdentifier(Ident(v));
            }
            else
            {
                System.Diagnostics.Debug.Assert(ai.Object is INamed, "Identifier must refere to something implementing INamed");
                writer.WriteIdentifier(Ident(ai.Object as INamed));
            }
        }

        private void GenerateIndexOperator(Namespace nspace, IndexOperator indexOperator, BufferedJavaScriptWriter writer)
        {
            this.GenerateExpression(nspace, indexOperator.Operator, writer, false, indexOperator.GetPrecedence());
            writer.WriteOperator("[");
            this.GenerateExpression(nspace, indexOperator.Index, writer);
            writer.WriteOperator("]");
        }

        private void GenerateXmlTag(Namespace nspace, XmlTag xmlTag, BufferedJavaScriptWriter writer)
        {
            var dummy = writer + "function" + "(" + ")" + "{";

            int level = 0;
            string var = this.GenerateXmlTag(nspace, xmlTag, writer, ref level);

            writer.WriteIdentifier("return");
            writer.WriteIdentifier(var);
            writer.WriteOperator(";");

            writer.WriteOperator("}");
            
            writer.WriteOperator("(");
            writer.WriteOperator(")");
        }

        private string GenerateXmlTag(Namespace nspace, XmlTag xmlTag, BufferedJavaScriptWriter writer, ref int level)
        {
            // The XML Tag is used as a way of instantiating an object?
            if (!xmlTag.IsHTML)
            {
                string var2 = "$x" + level.ToString();
                Constructor ctor = (xmlTag.Type as Class).GetDefaultConstructor();
                level++;
                // Create the tag
                var dummy2 = writer + "var" + var2 + "=" + "new" + GetQualifiedName(xmlTag.Type as Class) + "(" + ")";
                if (!ctor.IsGenericConstructor)
                {
                    dummy2 = writer + "." + Ident(ctor) + "(" + ")";
                }
                writer.WriteOperator(";");

                // Set all attributes
                foreach (XmlAttribute attr in xmlTag.Attributes.Values)
                {
                    string name = attr.Name;
                    if (attr.Var is Property)
                    {
                        Property prop = attr.Var as Property;
                        dummy2 = writer + var2 + "." + Ident(prop.Setter) + "(";
                        if (attr.StringValue != null)
                            writer.WriteString(attr.StringValue);
                        else
                            this.GenerateExpression(nspace, attr.OperatorValue, writer);
                        writer.WriteOperator(")");
                        writer.WriteOperator(";");
                    }
                    else
                    {
                        dummy2 = writer + var2 + "." + Ident(attr.Var) + "=";
                        if (attr.StringValue != null)
                            writer.WriteString(attr.StringValue);
                        else
                            this.GenerateExpression(nspace, attr.OperatorValue, writer);
                        writer.WriteOperator(";");
                    }
                }

                return var2;
            }

            //
            // The XML tag is a HTML tag
            //

            string var = "$x" + level.ToString();
            level++;
            // Create the tag
            var dummy = writer + "var" + var + "=" + "document" + "." + "createElement" + "(";
            writer.WriteString(xmlTag.Name);
            dummy = writer + ")" + ";";

            // Set all attributes
            foreach (XmlAttribute attr in xmlTag.Attributes.Values)
            {
                // Attach to an event?
                if (attr.Event != null)
                {
                    dummy = writer + var + "." + "addEventListener" + "(";
                    writer.WriteString(attr.Event.Name);
                    writer.WriteOperator(",");
                    this.GenerateExpression(nspace, attr.OperatorValue, writer);
                    dummy = writer + "," + false + ")" + ";";
                    continue;
                }
                else
                {
                    string name = attr.Name;
                    if (attr.Name == "class")
                        name = "className";
                    // Split the style string and assign the single CSS styles separately
                    else if (attr.Name == "style")
                    {
                        if (attr.StringValue != null)
                        {
                            string[] props = attr.StringValue.Split(';');
                            for (int i = 0; i < props.Length; ++i)
                            {
                                string prop = props[i].Trim();
                                int pos = prop.IndexOf(":");
                                if (pos == -1)
                                {
                                    // TODO: Produce CSS error
                                    continue;
                                }
                                string key = prop.Substring(0, pos).Trim();
                                string val = prop.Substring(pos + 1).Trim();
                                while ((pos = key.IndexOf("-")) != -1)
                                {
                                    if (pos + 1 < key.Length)
                                        key = key.Substring(0, pos) + key[pos + 1].ToString().ToUpper() + key.Substring(pos + 2);
                                    else
                                        // TODO: CSS error
                                        break;
                                }
                                dummy = writer + var + "." + attr.Name + "." + key + "=";
                                writer.WriteString(val);
                                writer.WriteOperator(";");
                            }
                        }
                        // TODO: What about operator values?
                        continue;
                    }

                    dummy = writer + var + "." + attr.Name + "=";
                    if (attr.OperatorValue != null)
                        this.GenerateExpression(nspace, attr.OperatorValue, writer);
                    else
                        writer.WriteString(attr.StringValue);
                    writer.WriteOperator(";");
                }
            }

            // Create all children
            foreach (object obj in xmlTag.Content)
            {
                if (obj is XmlTag)
                {
                    string cvar = this.GenerateXmlTag(nspace, (obj as XmlTag), writer, ref level);
                    dummy = writer + var + "." + "appendChild" + "(" + cvar + ")" + ";";
                }
                else if (obj is XmlText)
                {
                    dummy = writer + var + "." + "appendChild" + "(" + "document" + "." + "createTextNode" + "(";
                    writer.WriteString((obj as XmlText).Value);
                    dummy = writer + ")" + ")" + ";";
                }
                else if (obj is XmlTagFunction)
                {
                    dummy = writer + var + "." + "appendChild" + "(";
                    this.GenerateExpression(nspace, (obj as XmlTagFunction).Operator, writer);
                    dummy = writer + ")" + ";";
                }
                else if (obj is XmlTextFunction)
                {
                    dummy = writer + var + "." + "appendChild" + "(" + "document" + "." + "createTextNode" + "(";
                    this.GenerateExpression(nspace, (obj as XmlTextFunction).Operator, writer);
                    dummy = writer + ")" + ")" + ";";
                }
                else
                    System.Diagnostics.Debug.Assert(false, "Unknown XmlTag content");
            }

            return var;
        }

        private void GenerateFunctionOperator(Namespace nspace, FunctionOperator functionOperator, BufferedJavaScriptWriter writer)
        {
            if (!functionOperator.IsStatic)
            {
                writer.WriteIdentifier("$System.inlineFunc");
                writer.WriteOperator("(");
            }

            bool oldit = this.inIterator;
            if (functionOperator.IsIterator)
            {
                this.inIterator = true;
                var dummy2 = writer + "function" + "(" + ")" + "{" + "var" + "$s" + "=" + "{" + "$l" + ":" + "0" + "}" + ";" + "return";
            }

            var dummy = writer + "function" + "(";
            bool first = true;
            for (var i = 0; i < functionOperator.Parameters.Count; ++i)
            {
                if (first)
                    first = false;
                else
                    writer.WriteOperator(",");
                writer.WriteIdentifier(Ident(functionOperator.Parameters[i]));
            }
            dummy = writer + ")" + "{";

            if (functionOperator.IsIterator)
                dummy = writer + "while" + "(" + "true" + ")" + "{" + "switch" + "(" + "$s" + "." + "$l" + ")" + "{";

            if (functionOperator.IsIterator)
            {
                BufferedJavaScriptIteratorWriter w = new BufferedJavaScriptIteratorWriter();
                this.GenerateIteratorStatementList(nspace, functionOperator.Statements, w, true);
                writer.Write(w);
            }
            else
                this.GenerateStatementList(nspace, functionOperator.Statements, writer);
            writer.WriteOperator("}");

            if (functionOperator.IsIterator)
            {
                var dummy2 = writer + "}" + "}" + "}" + "(" + ")";
            }
            this.inIterator = oldit;

            if (!functionOperator.IsStatic)
            {
                writer.WriteOperator(",");
                writer.WriteIdentifier("this");
                writer.WriteOperator(")");
            }
        }

        private void GenerateInlineFunctionOperator(Namespace nspace, InlineFunctionOperator inlineFunctionOperator, BufferedJavaScriptWriter writer)
        {
            if (!inlineFunctionOperator.IsStatic)
            {
                writer.WriteIdentifier("$System.inlineFunc");
                writer.WriteOperator("(");
                writer.WriteIdentifier("this");
                writer.WriteOperator(",");
            }

            var dummy = writer + "function" + "(";
            bool first = true;
            for (var i = 0; i < inlineFunctionOperator.Parameters.Count; ++i)
            {
                if (first)
                    first = false;
                else
                    writer.WriteOperator(",");
                writer.WriteIdentifier(Ident(inlineFunctionOperator.Parameters[i]));
            }
            dummy = writer + ")" + "{" + "return";

            this.GenerateExpression(nspace, inlineFunctionOperator.Operator, writer);
            writer.WriteOperator("}");

            if (!inlineFunctionOperator.IsStatic)
                writer.WriteOperator(")");            
        }

        private void GenerateFunctionObjectOperator(Namespace nspace, FunctionObjectOperator functionObjectOperator, BufferedJavaScriptWriter writer)
        {
            MemberFunction func = null;
            if (functionObjectOperator.Function is AtomIdentifier && (functionObjectOperator.Function as AtomIdentifier).Object is MemberFunction)
                func = (functionObjectOperator.Function as AtomIdentifier).Object as MemberFunction;
            else if (functionObjectOperator.Function is PropertyOperator && (functionObjectOperator.Function as PropertyOperator).Object is MemberFunction)
                func = (functionObjectOperator.Function as PropertyOperator).Object as MemberFunction;
            else
                System.Diagnostics.Debug.Assert(false, "Unexpected occurence of FunctionObjectOperator");

            // If the function is static then there is nothing to do
            if (func.IsStatic)
            {
                this.GenerateExpression(nspace, functionObjectOperator.Function, writer);
                return;
            }

            JavaScriptWriter dummy = null;
            dummy = writer + "$System.funcObj" + "(";
            writer.WriteString(Ident(func));
            writer.WriteOperator(",");
            // Wrap it in a function
            if (functionObjectOperator.Function is AtomIdentifier)
                dummy = writer + "this";
            else // Property
            {
                PropertyOperator pop = functionObjectOperator.Function as PropertyOperator;
                this.GenerateExpression(nspace, pop.Operator, writer);
            }
            dummy = writer + ")";
        }

        private void GenerateDictOperator(Namespace nspace, DictContentOperator dictContentOperator, BufferedJavaScriptWriter writer)
        {
            var dummy = writer + "{";
            bool first = true;
            foreach ( string str in dictContentOperator.Values.Keys)
            {
                Operator op = dictContentOperator.Values[str];
                if (first)
                    first = false;
                else
                    writer.WriteOperator(",");
                writer.WriteString(str);
                writer.WriteOperator(":");
                this.GenerateExpression(nspace, op, writer);
            }
            dummy = writer + "}";
        }

        private void GenerateListOperator(Namespace nspace, ListContentOperator listContentOperator, BufferedJavaScriptWriter writer)
        {
            var dummy = writer + "[";
            bool first = true;
            foreach (Operator op in listContentOperator.Values)
            {
                if (first)
                    first = false;
                else
                    writer.WriteOperator(",");
                this.GenerateExpression(nspace, op, writer);
            }
            dummy = writer + "]";
        }

        private void GenerateAsOperator(Namespace nspace, AsOperator asOperator, BufferedJavaScriptWriter writer)
        {
            System.Diagnostics.Debug.Assert(asOperator.Type is AbstractType, "Can only cast to a class type currently");
            var dummy = writer + "$System.as" + "(";
            this.GenerateExpression(nspace, asOperator.Operator, writer);
            dummy = writer + "," + GetQualifiedName(asOperator.Type as AbstractType) + ")";
        }

        private void GenerateIsOperator(Namespace nspace, IsOperator isOperator, BufferedJavaScriptWriter writer)
        {
            System.Diagnostics.Debug.Assert(isOperator.Type is AbstractType, "Can only cast to a class type currently");
            var dummy = writer + "$System.isA" + "(";
            this.GenerateExpression(nspace, isOperator.Operator, writer);
            dummy = writer + "," + GetQualifiedName(isOperator.Type as AbstractType) + ")";
        }

        private void GeneratePropertyOperator(Namespace nspace, PropertyOperator propOperator, BufferedJavaScriptWriter writer, bool propertyReference)
        {
            if (propOperator.Object is MemberFunction)
            {
                MemberFunction func = propOperator.Object as MemberFunction;
                JSNativeFunctionAttribute attrib = func.GetAttribute<JSNativeFunctionAttribute>();
                if (attrib != null)
                {
                    if (func.IsStatic)
                    {
                        var dummy = writer + attrib.JSCode;
                    }
                    else
                    {
                        this.GenerateExpression(nspace, propOperator.Operator, writer, false, propOperator.GetPrecedence());
                        writer.WriteOperator(".");
                        writer.WriteIdentifier(attrib.JSCode);
                    }
                    return;
                }
                // The source code looks like "super.foobar" ?
                else if (propOperator.Operator is Atom && (propOperator.Operator as Atom).Kind == AtomKind.SUPER)
                {
                    writer.WriteIdentifier("this");
                    writer.WriteOperator(".");
                    writer.WriteIdentifier(Ident(propOperator.Object, true));
                    return;
                }
            }
            else if (propOperator.Object is Property)
            {
                Property prop = propOperator.Object as Property;
                if (propertyReference)
                {
                    // Return only the object. We will call a setter
                    this.GenerateExpression(nspace, propOperator.Operator, writer, false, propOperator.GetPrecedence());
                    return;
                }

                this.GenerateExpression(nspace, propOperator.Operator, writer, false, propOperator.GetPrecedence());
                HTMLPropertyAttribute attrib = prop.GetAttribute<HTMLPropertyAttribute>();
                JSNativePropertyAttribute attrib2 = prop.GetAttribute<JSNativePropertyAttribute>();
                if (attrib != null)
                {
                    var dummy = writer + "." + attrib.HTMLName;
                }
                else if (attrib2 != null)
                {
                    if (attrib2.IsMember)
                    {
                        var dummy = writer + "." + attrib2.JSGetterCode;
                    }
                    else
                    {
                        var dummy = writer + "." + attrib2.JSGetterCode + "(" + "this" + ")";
                    }
                }
                else
                {
                    var dummy = writer + "." + Ident(prop.Getter) + "(" + ")";
                }
                return;
            }
            else if (propOperator.Object is MemberEvent && propertyReference)
            {
                this.GenerateExpression(nspace, propOperator.Operator, writer, false, propOperator.GetPrecedence());
                return;
            }
            else if (propOperator.Object is EnumItem)
            {
                EnumItem item = propOperator.Object as EnumItem;

                JSNativeEnumItemAttribute attrib = item.GetAttribute<JSNativeEnumItemAttribute>();
                if (attrib != null)
                {
                    var dummy = writer + "(" + attrib.JSCode + ")";
                }
                else
                    this.GenerateExpression(nspace, item.InitialValue, writer);
                return;
            }

            this.GenerateExpression(nspace, propOperator.Operator, writer, false, propOperator.GetPrecedence());
            writer.WriteOperator(".");
            writer.WriteIdentifier(Ident(propOperator.Object));
        }

        private void GenerateIncDecOperator(Namespace nspace, IncDecOperator incDecOperator, BufferedJavaScriptWriter writer)
        {
            Property prop = null;
            if (incDecOperator.Operator is AtomIdentifier && (incDecOperator.Operator as AtomIdentifier).Object is Property)
                prop = (incDecOperator.Operator as AtomIdentifier).Object as Property;
            else if (incDecOperator.Operator is PropertyOperator && (incDecOperator.Operator as PropertyOperator).Object is Property)
                prop = (incDecOperator.Operator as PropertyOperator).Object as Property;

            if (prop != null)
            {
                JavaScriptWriter dummy = null;
                switch (incDecOperator.Kind)
                {
                    case IncDecOperatorKind.PostDec:
                        dummy = writer + "$System.op_postdec" + "(";
                        break;
                    case IncDecOperatorKind.PostInc:
                        dummy = writer + "$System.op_postinc" + "(";
                        break;
                    case IncDecOperatorKind.PreDec:
                        dummy = writer + "$System.op_predec" + "(";
                        break;
                    case IncDecOperatorKind.PreInc:
                        dummy = writer + "$System.op_preinc" + "(";
                        break;
                }
                this.GenerateExpression(nspace, incDecOperator.Operator, writer, true);
                writer.WriteOperator(",");
                writer.WriteString(Ident(prop.Getter));
                writer.WriteOperator(",");
                writer.WriteString(Ident(prop.Setter));
                writer.WriteOperator(")");
                return;
            }

            switch (incDecOperator.Kind)
            {
                case IncDecOperatorKind.PostDec:
                    this.GenerateExpression(nspace, incDecOperator.Operator, writer, false, incDecOperator.GetPrecedence());
                    writer.WriteOperator("--");
                    break;
                case IncDecOperatorKind.PostInc:
                    this.GenerateExpression(nspace, incDecOperator.Operator, writer, false, incDecOperator.GetPrecedence());
                    writer.WriteOperator("++");
                    break;
                case IncDecOperatorKind.PreDec:
                    writer.WriteOperator("--");
                    writer.WriteOperator("(");
                    this.GenerateExpression(nspace, incDecOperator.Operator, writer, false, incDecOperator.GetPrecedence());
                    writer.WriteOperator(")");
                    break;
                case IncDecOperatorKind.PreInc:
                    writer.WriteOperator("++");
                    writer.WriteOperator("(");
                    this.GenerateExpression(nspace, incDecOperator.Operator, writer, false, incDecOperator.GetPrecedence());
                    writer.WriteOperator(")");
                    break;
            }
        }

        private void GenerateNotOperator(Namespace nspace, NotOperator notOperator, BufferedJavaScriptWriter writer)
        {
            writer.WriteOperator("!");
            this.GenerateExpression(nspace, notOperator.Operator, writer, false, notOperator.GetPrecedence());
        }

        private void GeneratePlusOperator(Namespace nspace, PlusOperator plusOperator, BufferedJavaScriptWriter writer)
        {
            writer.WriteOperator("+");
            this.GenerateExpression(nspace, plusOperator.Operator, writer, false, plusOperator.GetPrecedence());
        }

        private void GenerateMinusOperator(Namespace nspace, MinusOperator minusOperator, BufferedJavaScriptWriter writer)
        {
            writer.WriteOperator("-");
            this.GenerateExpression(nspace, minusOperator.Operator, writer, false, minusOperator.GetPrecedence());
        }

        private void GenerateInvertOperator(Namespace nspace, InvertOperator invertOperator, BufferedJavaScriptWriter writer)
        {
            writer.WriteOperator("~");
            this.GenerateExpression(nspace, invertOperator.Operator, writer, false, invertOperator.GetPrecedence());
        }

        private void GenerateFunctionCallOperator(Namespace nspace, FunctionCallOperator functionCallOperator, BufferedJavaScriptWriter writer)
        {
            // Emit an event
            if (functionCallOperator.Event != null)
            {
                var dummy = writer + "$System.emitEvent" + "(";
                this.GenerateExpression(nspace, functionCallOperator.Operator, writer, false, functionCallOperator.GetPrecedence());
                writer.WriteOperator(",");
                this.GenerateExpression(nspace, functionCallOperator.Parameters[0], writer);
                writer.WriteOperator(")");
                return;
            }

            this.GenerateExpression(nspace, functionCallOperator.Operator, writer, false, functionCallOperator.GetPrecedence());

            // Nothing has to be done if we call the generic constructor only.
            if (functionCallOperator.FunctionType is Constructor && (functionCallOperator.FunctionType as Constructor).IsGenericConstructor)
                return;

            writer.WriteOperator("(");
            bool first = true;
            foreach (Operator op in functionCallOperator.Parameters)
            {
                if (first)
                    first = false;
                else
                    writer.WriteOperator(",");
                this.GenerateExpression(nspace, op, writer);
            }
            writer.WriteOperator(")");
        }

        private void GenerateNewOperator(Namespace nspace, NewOperator newOperator, BufferedJavaScriptWriter writer)
        {   
            System.Diagnostics.Debug.Assert(newOperator.Type is Class, "The new operator can only instantiate classes");
            var dummy = writer + "new" + GetQualifiedName(newOperator.Type as Class) + "(" + ")";
            if (!newOperator.Constructor.IsGenericConstructor)
            {
                dummy = writer + "." + Ident(newOperator.Constructor);
            }
        }

        private void GenerateBinaryOperator(Namespace nspace, BinaryOperator op, BufferedJavaScriptWriter writer)
        {
            Property prop = null;
            if (op.Left is AtomIdentifier && (op.Left as AtomIdentifier).Object is Property)
                prop = (op.Left as AtomIdentifier).Object as Property;
            else if (op.Left is PropertyOperator && (op.Left as PropertyOperator).Object is Property)
                prop = (op.Left as PropertyOperator).Object as Property;

            // Do we assign to a property? (which is not a HTML property)
            if (prop != null && prop.GetAttribute<HTMLPropertyAttribute>() == null)
            {
                switch (op.Kind)
                {
                    case BinaryOperatorKind.ASSIGN:
                        {
                            this.GenerateExpression(nspace, op.Left, writer, true, op.GetPrecedence());
                            var dummy = writer + "." + Ident(prop.Setter) + "(";
                            this.GenerateExpression(nspace, op.Right, writer);
                            writer.WriteOperator(")");
                            return;
                        }
                    case BinaryOperatorKind.ASSIGNADD:
                        {
                            var dummy = writer + "$System" + "." + "op_assignadd" + "(";
                            this.GenerateExpression(nspace, op.Left, writer, true, op.GetPrecedence());
                            writer.WriteOperator(",");
                            dummy = writer + "\"" + Ident(prop.Getter) + "\"";
                            writer.WriteOperator(",");
                            dummy = writer + "\"" + Ident(prop.Setter) + "\"";
                            writer.WriteOperator(",");
                            this.GenerateExpression(nspace, op.Right, writer);
                            writer.WriteOperator(")");
                            return;
                        }
                    case BinaryOperatorKind.ASSIGNAND:
                        // TODO
                        break;
                    case BinaryOperatorKind.ASSIGNDIV:
                        // TODO
                        break;
                    case BinaryOperatorKind.ASSIGNMUL:
                        // TODO
                        break;
                    case BinaryOperatorKind.ASSIGNOR:
                        // TODO
                        break;
                    case BinaryOperatorKind.ASSIGNSUB:
                        // TODO
                        break;
                    case BinaryOperatorKind.ASSIGNXOR:
                        // TODO
                        break;
                }
            }

            this.GenerateExpression(nspace, op.Left, writer, false, op.GetPrecedence());
            switch (op.Kind)
            {
                case BinaryOperatorKind.ADD:
                    writer.WriteOperator("+");
                    break;
                case BinaryOperatorKind.AND:
                    writer.WriteOperator("&&");
                    break;
                case BinaryOperatorKind.ARITHAND:
                    writer.WriteOperator("&");
                    break;
                case BinaryOperatorKind.ARITHOR:
                    writer.WriteOperator("|");
                    break;
                case BinaryOperatorKind.ARITHXOR:
                    writer.WriteOperator("^");
                    break;
                case BinaryOperatorKind.ASSIGN:
                    writer.WriteOperator("=");
                    break;
                case BinaryOperatorKind.ASSIGNADD:
                    writer.WriteOperator("+=");
                    break;
                case BinaryOperatorKind.ASSIGNAND:
                    writer.WriteOperator("&=");
                    break;
                case BinaryOperatorKind.ASSIGNDIV:
                    writer.WriteOperator("/=");
                    break;
                case BinaryOperatorKind.ASSIGNMUL:
                    writer.WriteOperator("*=");
                    break;
                case BinaryOperatorKind.ASSIGNOR:
                    writer.WriteOperator("|=");
                    break;
                case BinaryOperatorKind.ASSIGNSUB:
                    writer.WriteOperator("-=");
                    break;
                case BinaryOperatorKind.ASSIGNXOR:
                    writer.WriteOperator("^=");
                    break;
                case BinaryOperatorKind.DIV:
                    writer.WriteOperator("/");
                    break;
                case BinaryOperatorKind.EQUAL:
                    writer.WriteOperator("==");
                    break;
                case BinaryOperatorKind.GREATER:
                    writer.WriteOperator(">");
                    break;
                case BinaryOperatorKind.GREATEROREQUAL:
                    writer.WriteOperator(">=");
                    break;
                case BinaryOperatorKind.LEFT_SHIFT:
                    writer.WriteOperator("<<");
                    break;
                case BinaryOperatorKind.LESS:
                    writer.WriteOperator("<");
                    break;
                case BinaryOperatorKind.LESSOREQUAL:
                    writer.WriteOperator("<=");
                    break;
                case BinaryOperatorKind.MUL:
                    writer.WriteOperator("*");
                    break;
                case BinaryOperatorKind.NOTEQUAL:
                    writer.WriteOperator("!=");
                    break;
                case BinaryOperatorKind.OR:
                    writer.WriteOperator("||");
                    break;
                case BinaryOperatorKind.RIGHT_SHIFT:
                    writer.WriteOperator(">>");
                    break;
                case BinaryOperatorKind.SUB:
                    writer.WriteOperator("-");
                    break;
                case BinaryOperatorKind.MODULO:
                    writer.WriteOperator("%");
                    break;
            }
            this.GenerateExpression(nspace, op.Right, writer, false, op.GetPrecedence());
        }

        private void GenerateStatementList(Namespace nspace, StatementList list, BufferedJavaScriptWriter writer)
        {
            if (list == null)
                return;
            foreach (Statement s in list)
            {
                this.GenerateStatement(nspace, s, writer);
            }
        }

        private void GenerateStatement(Namespace nspace, Statement s, BufferedJavaScriptWriter writer)
        {
            if (this.debug)
            {
                var dummy = writer + "$L" + "=" + s.Node.StartLine + ";";
            }

            if (s is VarStatement)
            {
                VarStatement vs = s as VarStatement;
                if (vs.InitialValue == null)
                {
                    var dummy = writer + "var" + Ident(vs) + ";";
                }
                else
                {
                    var dummy = writer + "var" + Ident(vs) + "=";
                    this.GenerateExpression(nspace, vs.InitialValue, writer);
                    writer.WriteOperator(";");
                }
            }
            else if (s is IfStatement)
            {
                IfStatement f = s as IfStatement;
                while (f != null)
                {
                    if (f != s)
                        writer.WriteIdentifier("else");
                    if (f.Condition != null)
                    {
                        var dummy2 = writer + "if" + "(";
                        this.GenerateExpression(nspace, f.Condition, writer);
                        writer.WriteOperator(")");
                    }
                    writer.WriteOperator("{");
                    if (f.Statements != null)
                        this.GenerateStatementList(nspace, f.Statements, writer);
                    var dummy = writer + "}";
                    f = f.Else;
                }
            }
            else if (s is WhileStatement)
            {
                WhileStatement w = s as WhileStatement;
                if (w.IsDoWhileLoop)
                {
                    var dummy = writer + "do" + "{";
                    if (w.Statements != null)
                        this.GenerateStatementList(nspace, w.Statements, writer);
                    dummy = writer + "}" + "while" + "(";
                    this.GenerateExpression(nspace, w.Condition, writer);
                    dummy = writer + ")" + ";";
                }
                else
                {
                    var dummy = writer + "while" + "(";
                    this.GenerateExpression(nspace, w.Condition, writer);
                    dummy = writer + ")" + "{";
                    if (w.Statements != null)
                        this.GenerateStatementList(nspace, w.Statements, writer);
                    dummy = writer + "}";
                }
            }
            else if (s is ForStatement)
            {
                ForStatement f = s as ForStatement;
                var dummy = writer + "for" + "(";
                if (f.Initialization != null)
                {
                    this.GenerateStatement(nspace, f.Initialization, writer);
                }
                else
                    writer.WriteOperator(";");
                if (f.Condition != null)
                {
                    this.GenerateExpression(nspace, f.Condition, writer);
                }
                writer.WriteOperator(";");
                if (f.Increment != null)
                {
                    this.GenerateExpression(nspace, f.Increment, writer);
                }
                writer.WriteOperator(")");
                writer.WriteOperator("{");
                if (f.Statements != null)
                    this.GenerateStatementList(nspace, f.Statements, writer);
                writer.WriteOperator("}");
            }
            else if (s is ForEachStatement)
            {
                ForEachStatement f = s as ForEachStatement;
                if (f.UsesAnIterator)
                {
                    TmpVariable tmp1 = new TmpVariable();
                    TmpVariable tmp2 = new TmpVariable();
                    var dummy = writer + "var" + Ident(tmp1) + "=";
                    this.GenerateExpression(nspace, f.Content, writer);
                    dummy = writer + ";" + "var" + Ident(f.Var) + ";" ;
                    dummy = writer + "while" + "(" + "typeof" + "(" + Ident(f.Var) + "=" + Ident(tmp1) + "(" + ")" + ")" + "!=";
                    writer.WriteString("undefined");
                    dummy = writer + ")" + "{";
                }
                else
                {
                    TmpVariable tmp1 = new TmpVariable();
                    TmpVariable tmp2 = new TmpVariable();
                    var dummy = writer + "var" + Ident(tmp2) + "=";
                    this.GenerateExpression(nspace, f.Content, writer);
                    dummy = writer + ";" + "for" + "(" + "var" + Ident(tmp1) + "=" + "0" + ";" + Ident(tmp1) + "<" + Ident(tmp2) + "." + "length" + ";" + Ident(tmp1) + "++" + ")";
                    writer.WriteOperator("{");
                    dummy = writer + "var" + Ident(f.Var) + "=" + Ident(tmp2) + "[" + Ident(tmp1) + "]" + ";";
                }
                if (f.Statements != null)
                    this.GenerateStatementList(nspace, f.Statements, writer);
                writer.WriteOperator("}");
            }
            else if (s is ReturnStatement)
            {
                if (this.context is PropertySetter)
                {
                    PropertySetter setter = this.context as PropertySetter;
                    var dummy = writer + "if" + "(" + "this" + "." + (Ident(setter.Property) + "_") + ")" + "$System.publishProperty.call";
                    dummy = writer + "(" + "this" + "." + (Ident(setter.Property) + "_") + "," + "this" + "." + Ident(setter.Property) + ")" + ";";
                }

                writer.WriteIdentifier("return");
                if ( (s as ReturnStatement).Operator != null )
                    this.GenerateExpression(nspace, (s as ReturnStatement).Operator, writer);
                writer.WriteOperator(";");
            }
            else if (s is ExpressionStatement)
            {
                this.GenerateExpression(nspace, (s as ExpressionStatement).Operator, writer);
                writer.WriteOperator(";");
            }
            else if (s is BreakStatement)
            {
                writer.WriteIdentifier("break");
                writer.WriteOperator(";");
            }
            else if (s is ContinueStatement)
            {
                writer.WriteIdentifier("continue");
                writer.WriteOperator(";");
            }
            else if (s is ThrowStatement)
            {
                writer.WriteIdentifier("throw");
                this.GenerateExpression(nspace, (s as ThrowStatement).Operator, writer);
                writer.WriteOperator(";");
            }
            // TODO: Try catch finally
            // TODO: switch case
            else
                System.Diagnostics.Debug.Assert(false, "Unimplemented kind of statement");
        }

        private void GenerateIteratorStatementList(Namespace nspace, StatementList list, BufferedJavaScriptIteratorWriter writer, bool closeWithThrow)
        {
            if (list == null)
                return;

            if ( closeWithThrow )
                writer.CreateInitialLabel();

            foreach (Statement s in list)
            {
                this.GenerateIteratorStatement(nspace, s, writer);
            }

            if (closeWithThrow)
            {
                writer.WriteIdentifier("return");
                writer.WriteIdentifier("undefined");
                writer.WriteOperator(";");
            }
        }

        private void GenerateIteratorStatement(Namespace nspace, Statement s, BufferedJavaScriptIteratorWriter writer)
        {
            if (s is VarStatement)
            {
                VarStatement vs = s as VarStatement;
                if (vs.IsInInterator)
                {
                    if (vs.InitialValue == null)
                    {
                        var dummy = writer + "$s" + "." + Ident(vs) + "=" + "null" + ";";
                    }
                    else
                    {
                        var dummy = writer + "$s" + "." + Ident(vs) + "=";
                        this.GenerateExpression(nspace, vs.InitialValue, writer);
                        writer.WriteOperator(";");
                    }
                }
                else
                {
                    this.GenerateStatement(nspace, s, writer);
                    return;
                }
            }
            else if (s is IfStatement)
            {
                IfStatement f = s as IfStatement;

                CaseLabel end = writer.CreateLabel();
                CaseLabel c = null;

                while (f != null)
                {
                    if (c != null)
                        writer.FixLabel(c);
                    c = writer.CreateLabel();
                    if (f.Condition != null)
                    {
                        var dummy2 = writer + "if" + "(" + "!" + "(";
                        this.GenerateExpression(nspace, f.Condition, writer);
                        dummy2 = writer + ")" + ")" + "{" + "$s" + "." + "$l" + "=";
                        writer.Write(c);
                        dummy2 =  writer +";" + "break" + ";" + "}";
                    }

                    if (f.Statements != null)
                        this.GenerateIteratorStatementList(nspace, f.Statements, writer, false);
                    
                    if ( f.Else != null )
                    {
                        var dummy = writer + "$s" + "." + "$l" + "=";
                        writer.Write(end);
                        dummy = writer + ";" + "break" + ";";
                        f = f.Else;
                    }
                    else
                        break;
                }
                writer.FixLabel(c);
                writer.FixLabel(end);
            }
            else if (s is WhileStatement)
            {
                WhileStatement w = s as WhileStatement;

                this.iteratorContinueCaseLabel = writer.CreateLabel();
                this.iteratorBreakCaseLabel = writer.CreateLabel();
                writer.FixLabel(this.iteratorContinueCaseLabel);

                if (w.IsDoWhileLoop)
                {
                    if (w.Statements != null)
                        this.GenerateIteratorStatementList(nspace, w.Statements, writer, false);

                    // If we have to leave the loop ...
                    var dummy = writer + "if" + "(";
                    this.GenerateExpression(nspace, w.Condition, writer);
                    dummy = writer + ")" + "{" + "$s" + "." + "$l" + "=";
                    writer.Write(this.iteratorContinueCaseLabel);
                    dummy = writer + ";" + "break" + ";" + "}";
                }
                else
                {
                    // If we have to leave the loop ...
                    var dummy = writer + "if" + "(" + "!" + "(";
                    this.GenerateExpression(nspace, w.Condition, writer);
                    dummy = writer + ")" + ")" + "{" + "$s" + "." + "$l" + "=";
                    writer.Write(this.iteratorBreakCaseLabel);
                    dummy = writer +";" + "break" + ";" + "}";

                    if (w.Statements != null)
                        this.GenerateIteratorStatementList(nspace, w.Statements, writer, false);

                    // Jump back to the loop head
                    dummy = writer + "$s" + "." + "$l" + "=";
                    writer.Write(this.iteratorContinueCaseLabel);
                    dummy = writer +";" + "break" + ";";
                }

                writer.FixLabel(this.iteratorBreakCaseLabel);
                this.iteratorBreakCaseLabel = null;
                this.iteratorContinueCaseLabel = null;
                return;
            }
            else if (s is ForStatement)
            {
                this.iteratorContinueCaseLabel = writer.CreateLabel();
                this.iteratorBreakCaseLabel = writer.CreateLabel();

                ForStatement f = s as ForStatement;
                if (f.Initialization != null)
                    this.GenerateIteratorStatement(nspace, f.Initialization, writer);
                writer.FixLabel(this.iteratorContinueCaseLabel);
                if (f.Condition != null)
                {
                    var dummy = writer + "if" + "(" + "!" + "(";
                    this.GenerateExpression(nspace, f.Condition, writer);
                    dummy = writer + ")" + ")" + "{" + "$s" + "." + "$l" + "=";
                    writer.Write(this.iteratorBreakCaseLabel);
                    dummy = writer + ";" + "break" + ";" + "}";
                }
                if (f.Statements != null)
                    this.GenerateIteratorStatementList(nspace, f.Statements, writer, false);
                if (f.Increment != null)
                {
                    this.GenerateExpression(nspace, f.Increment, writer);
                    writer.WriteOperator(";");
                }
                var dummy2 = writer + "$s" + "." + "$l" + "=";
                writer.Write(this.iteratorContinueCaseLabel);
                dummy2 = writer + ";" + "break" + ";";

                writer.FixLabel(this.iteratorBreakCaseLabel);
                this.iteratorBreakCaseLabel = null;
                this.iteratorContinueCaseLabel = null;
            }
            else if (s is ForEachStatement)
            {
                this.iteratorContinueCaseLabel = writer.CreateLabel();
                this.iteratorBreakCaseLabel = writer.CreateLabel();

                ForEachStatement f = s as ForEachStatement;
                if (f.UsesAnIterator)
                {
                    TmpVariable tmp1 = new TmpVariable();
                    TmpVariable tmp2 = new TmpVariable();
                    var dummy = writer + "$s" + "." + Ident(tmp1) + "=";
                    this.GenerateExpression(nspace, f.Content, writer);
                    dummy = writer + ";";
                    writer.FixLabel(this.iteratorContinueCaseLabel);
                    dummy = writer + "if" + "(" + "typeof" + "(" + "$s" + "." + Ident(f.Var) + "=" + "$s" + "." + Ident(tmp1) + "(" + ")" + ")" + "==";
                    writer.WriteString("undefined");
                    dummy = writer + ")" + "{" + "$s" + "." + "$l" + "=";
                    writer.Write(this.iteratorBreakCaseLabel);
                    dummy = writer + ";" + "break" + ";" + "}";
                }
                else
                {
                    TmpVariable tmp1 = new TmpVariable();
                    TmpVariable tmp2 = new TmpVariable();
                    var dummy = writer + "$s" + "." + Ident(tmp2) + "=";
                    this.GenerateExpression(nspace, f.Content, writer);
                    writer.WriteOperator(";");
                    dummy = writer + "$s" + "." + Ident(tmp1) + "=" + "0" + ";";
                    writer.FixLabel(this.iteratorContinueCaseLabel);
                    dummy = writer + "if" + "(" + "$s" + "." + Ident(tmp1) + ">=" + "$s" + "." + Ident(tmp2) + "." + "length" + ")" + "{";
                    dummy = writer + "$s" + "." + "$l" + "=";
                    writer.Write(this.iteratorBreakCaseLabel);
                    dummy = writer + ";" + "break" + ";" + "}";
                    dummy = writer + "$s" + "." + Ident(f.Var) + "=" + "$s" + "." + Ident(tmp2) + "[" + "$s" + "." + Ident(tmp1) + "++" + "]" + ";";
                }
                if (f.Statements != null)
                    this.GenerateIteratorStatementList(nspace, f.Statements, writer, false);

                var dummy2 = writer + "$s" + "." + "$l" + "=";
                writer.Write(this.iteratorContinueCaseLabel);
                dummy2 = writer + ";" + "break" + ";";

                writer.FixLabel(this.iteratorBreakCaseLabel);
                this.iteratorBreakCaseLabel = null;
                this.iteratorContinueCaseLabel = null;
            }
            else if (s is YieldReturnStatement)
            {
                var c = writer.CreateLabel();
                var dummy = writer + "$s" + "." + "$l" + "=";
                writer.Write(c);
                writer.WriteOperator(";");
                writer.WriteIdentifier("return");
                this.GenerateExpression(nspace, (s as YieldReturnStatement).Operator, writer);
                writer.WriteOperator(";");
                writer.FixLabel(c, true);
                return;
            }
            else if (s is YieldBreakStatement)
            {
                writer.WriteIdentifier("return");
                writer.WriteIdentifier("undefined");
                writer.WriteOperator(";");
            }
            else if (s is BreakStatement)
            {
                var dummy = writer + "$s" + "." + "$l" + "=";
                writer.Write(this.iteratorBreakCaseLabel);
                writer.WriteOperator(";");
            }
            else if (s is ContinueStatement)
            {
                var dummy = writer + "$s" + "." + "$l" + "=";
                writer.Write(this.iteratorContinueCaseLabel);
                writer.WriteOperator(";");
            }
            else
            {
                this.GenerateStatement(nspace, s, writer);
                return;
            }
        }
        // TODO: try catch finally
        // TODO: switch case
    }
}
